package com.eet.export.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.eet.export.constants.CommonConstants;
import com.eet.export.pojo.ExportExcelDto;
import com.eet.export.props.ExportProperties;
import com.eet.export.service.ExportService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Builder;
import lombok.Data;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;

/**
 * 导出服务实现类
 *
 * @author Administrator
 */
@Service
public class ExportServiceImpl implements ExportService {
    private final ExportFields exportFields;
    private ObjectMapper objectMapper;

    /**
     * 初始化参数
     *
     * @param properties 配置文件参数
     */
    public ExportServiceImpl(ExportProperties properties) {
        // 导出文件名
        String fileName = Objects.nonNull(properties.getFileName()) ? properties.getFileName() : String.valueOf(System.currentTimeMillis());
        // 单sheet页名称
        String sheetName = Optional.ofNullable(properties.getSheetNames()).flatMap(list -> list.stream().findFirst()).orElse(CommonConstants.DEFAULT_SHEET_NAME);
        // 设置Excel类型
        ExcelTypeEnum excelTypeEnum =  Objects.nonNull(properties.getExcelType()) ? ExcelTypeEnum.valueOf(properties.getExcelType()) : ExcelTypeEnum.XLSX;
        exportFields = ExportFields.builder()
                .fileName(fileName)
                .sheetName(sheetName)
                .excelTypeEnum(excelTypeEnum)
                .build();
    }

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Data
    @Builder
    private static class ExportFields {
        /**
         * 文件名称
         */
        private String fileName;

        /**
         * sheet页名称
         */
        private String sheetName;

        /**
         * Excel 类型
         */
        private ExcelTypeEnum excelTypeEnum;

        /**
         * 导出路由
         */
        private String exportPathUrl;

        /**
         * 表头
         */
        private List<List<String>> headList;

        /**
         * 表体
         */
        private List<List<Object>> tableRowData;
    }

    @Override
    public void dynamicExport(Map<String, String> selectColumn, List<?> dataList, HttpServletResponse response) throws IOException {
        // 表头数据
        List<List<String>> headList = convertHeadList(selectColumn);
        exportFields.setHeadList(headList);
        // 表体数据
        List<List<Object>> tableRowData = convertTableList(selectColumn, dataList);
        exportFields.setTableRowData(tableRowData);

        // 下载文件
        downloadFile(exportFields, response);
    }

    @Override
    public void dynamicExport(ExportExcelDto excelDto, HttpServletResponse response) throws IOException {
        if (Objects.isNull(excelDto)) {
            throw new NullPointerException(CommonConstants.EMPTY_DATA_EXCEPTION);
        }
        // 修改文件名
        if (Objects.nonNull(excelDto.getFileName())) {
            exportFields.setFileName(excelDto.getFileName());
        }
        // 修改sheet页名称
        if (!CollectionUtils.isEmpty(excelDto.getSheetNames())) {
            exportFields.setSheetName(excelDto.getSheetNames().get(0));
        }
        // 修改Excel类型
        if (Objects.nonNull(excelDto.getExcelTypeEnum())) {
            exportFields.setExcelTypeEnum(excelDto.getExcelTypeEnum());
        }
        dynamicExport(excelDto.getHeadMap(), excelDto.getTableData(), response);
    }

    /**
     * 文件下载
     *
     * @param exportFields 导出字段实体
     * @param response     响应实体
     * @throws IOException IO异常
     */
    private void downloadFile(ExportFields exportFields, HttpServletResponse response) throws IOException {
        // 导出文件名
        String fileName = exportFields.getFileName();

        // 设置响应头
        response.setContentType(CommonConstants.EXCEL_CONTENT_TYPE);
        // 对文件名进行编码
        String encodeName = URLEncoder.encode(fileName + exportFields.excelTypeEnum.getValue(), CommonConstants.CHARACTER_ENCODING);
        response.setCharacterEncoding(CommonConstants.CHARACTER_ENCODING);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, CommonConstants.ATTACHMENT_FILE + encodeName);
        response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);

        EasyExcel.write(response.getOutputStream())
                .autoCloseStream(true)
                .head(exportFields.getHeadList())
                .excelType(exportFields.excelTypeEnum)
                .sheet(exportFields.getSheetName())
                .registerWriteHandler(getWriterHandler())
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .doWrite(exportFields.getTableRowData());
    }

    /**
     * 设置单元格样式
     *
     * @return 单元格样式策略
     */
    private WriteHandler getWriterHandler() {
        // 头的策略
        WriteCellStyle headStyle = new WriteCellStyle();
        // 背景色
        headStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
        WriteFont headFont = new WriteFont();
        headFont.setFontHeightInPoints((short) 14);
        headStyle.setWriteFont(headFont);

        // 内容的策略
        WriteCellStyle contentStyle = new WriteCellStyle();
        // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
        contentStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        // 背景绿色
        contentStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        // 字体策略
        WriteFont contentFont = new WriteFont();
        contentFont.setFontHeightInPoints((short) 12);
        contentStyle.setWriteFont(contentFont);

        // 设置自动换行
        contentStyle.setWrapped(true);
        // 设置水平居中
        contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 设置边框样式
        contentStyle.setBorderLeft(BorderStyle.THIN);
        contentStyle.setBorderBottom(BorderStyle.THIN);
        contentStyle.setBorderRight(BorderStyle.THIN);
        contentStyle.setBorderTop(BorderStyle.THIN);
        return new HorizontalCellStyleStrategy(headStyle, contentStyle);
    }

    /**
     * 表头数据集转换
     *
     * @param selectMap 选中列集合
     * @return 表头数据集
     */
    private List<List<String>> convertHeadList(Map<String, String> selectMap) {
        if (CollectionUtils.isEmpty(selectMap)) {
            throw new NullPointerException(CommonConstants.NON_SELECTED_EXCEPTION);
        }
        // 动态添加所有表头行集合
        List<List<String>> headList = new ArrayList<>();

        // 表头字段集合
        List<String> titleList = new ArrayList<>();
        // 取Map中的VALUE为表头标题值
        selectMap.forEach((key, value) -> titleList.add(value));
        // 封装表头标题集合
        titleList.stream().filter(title -> !StringUtils.EMPTY.equals(title)).forEach(item -> {
            // 表头
            List<String> headTitle = new ArrayList<>();
            headTitle.add(item);
            headList.add(headTitle);
        });
        return headList;
    }


    /**
     * 表体数据集转换
     *
     * @param selectMap 选中列集合
     * @param data      初始数据
     * @return 转换后的表体数据
     */
    @SuppressWarnings("unchecked")
    private List<List<Object>> convertTableList(Map<String, String> selectMap, List<?> data) {
        if (CollectionUtils.isEmpty(selectMap)) {
            throw new NullPointerException(CommonConstants.NON_SELECTED_EXCEPTION);
        }
        if (CollectionUtils.isEmpty(data)) {
            throw new NullPointerException(CommonConstants.EMPTY_DATA_EXCEPTION);
        }
        // 表体数据集合
        List<List<Object>> tableData = new ArrayList<>();
        data.forEach(record -> {
            try {
                // 将Record实体数据转化为Map进行PUT
                String recordJson = objectMapper.writeValueAsString(record);
                Map<String, Object> objectMap = objectMapper.readValue(recordJson, LinkedHashMap.class);
                // 表体
                List<Object> rowData = new ArrayList<>();
                selectMap.forEach((key, value) -> rowData.add(objectMap.get(key)));
                tableData.add(rowData);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        });
        return tableData;
    }
}
